const Axios = require("axios");
const fs = require("fs");
const path = require("path");
const { checkConfigFile } = require("./file/index");
const createSetting = require("./lib/createSetting");

let options = {};

// console.log("createSetting.data", createSetting.data);
options = Object.assign(options, createSetting.data);
let typeEnum = {
  "integer": "string",
  "int32": "string",
  "int64": "string",
  "date-time": "number",
};
let globalComponents = {};
let globalTypeImport = [];
let globalResTypeImport = [];
async function init() {
  const { apiUrl } = options;
  try {
    const defaultConfig = await checkConfigFile();
    options = {
      ...options,
      ...(defaultConfig || {}),
    };

    // 合并枚举
    typeEnum = Object.assign(typeEnum, options.typeEnum || {});

    // console.log("typeEnum", typeEnum);

    const res = await Axios.get(apiUrl);
    console.log("----------api获取成功------------");
    const { paths = {}, components = {} } = res.data;
    globalComponents = components;
    const keys = Object.keys(paths || {});
    console.log("----------开始解析------------");
    // 所有types定义
    let typesFileContent = "";
    // 所有request
    let requestMethodStr = "";

    keys.forEach((key, index) => {
      // if (key !== "/v1/role/enable") return;
      // console.log(`----------解析第${index + 1}条------------`);
      const path = paths[key] || {};
      let types = "";
      let paramsType = "";
      Object.entries(path).forEach(([method, item]) => {
        const { requestBody, operationId, summary, parameters, responses } =
          item;
        // console.log(`----------当前url------------`, key, method);
        // console.log("46", requestBody, parameters);
        const { url, extra } = getMethodUrl(key);
        // console.log(url, extra);

        // 生成request params ts
        let typePName;
        if (parameters) {
          typePName = `${operationId}${convertToFirstCase(method)}ParamsType`;
          typePName = capitalizeFirstLetter(typePName);
          paramsType = typeParamsHandle(parameters, typePName, extra);
          globalTypeImport.push(typePName);
        }
        // types = transformType(p, typePName);

        // 生成request body ts
        let typeName;
        if (requestBody) {
          const r = typesHandle(requestBody, extra);
          typeName = `${operationId}${convertToFirstCase(method)}Type`;
          typeName = capitalizeFirstLetter(typeName);
          globalTypeImport.push(typeName);
          types = transformType(r, typeName);
        }

        // 生成response
        let name = `${operationId}${convertToFirstCase(method)}ResponseType`;
        name = capitalizeFirstLetter(name);
        let responseTs = responseHandle(responses, name);
        globalResTypeImport.push(name);
        // console.log("responseTs+++++++",responseTs)

        // const [requestType] = globalTypeImport.slice(-1);
        // const [responseType] = globalResTypeImport.slice(-1);
        requestMethodStr += getAllMethod({
          key,
          method,
          summary,
          requestBody: typeName,
          requestType: typePName,
          responseType: name,
        });

        responseTs =
          responseTs === "string" ? exportTsString(name) : responseTs;
        types = types === "string" ? exportTsString(typeName) : types;
        paramsType =
          paramsType === "string" ? exportTsString(typePName) : paramsType;

        typesFileContent += `
          ${responseTs || ""}
          ${types || ""}
          ${paramsType || ""}
        `;
      });
    });
    // console.log(typesFileContent);
    checkFolderExist();
    generateTypeFile(typesFileContent);
    generateApiFile(requestMethodStr);
    console.log("写入完成");
  } catch (error) {
    console.error(error);
  }
}

function exportTsString(name) {
  return `
export type ${name} = string;`;
}

function typeParamsHandle(parameters, name, extra = []) {
  // console.log("++parameters,extra++++", parameters, extra);
  if (!Array.isArray(parameters)) return;
  const properties = {};
  parameters.forEach((item) => {
    if (!extra.some((i) => i === item.name)) {
      let innerProperties = null;
      if (item["$ref"]) {
        innerProperties = refsHandle(item["$ref"]);
      }
      let type = item?.schema?.type || "any";
      if (item?.schema?.format) {
        type = typeEnum[item.schema.format];
      }
      properties[item.name] = {
        ...item,
        title: "",
        type,
        required: item.required,
      };

      if (innerProperties) {
        properties[key].schema = {
          properties: innerProperties,
        };
      }
    }
  });
  return wordsHandler(properties, [], name);
}

function responseHandle(response, name) {
  const refs = response?.["200"]?.content?.["*/*"]?.schema?.["$ref"] || "";
  const r = refsHandle(refs,name);
  return transformType(r, name);
}

function transformType(requestBody, name) {
 
  const refs =
    requestBody?.content?.["application/json"]?.schema?.["$ref"] || "";
    // console.log("+++++++++进入string+++++++",requestBody?.content?.["application/json"]?.schema);
  if (!refs && requestBody?.content?.["text/plain"]) {
    return "string";
  }
  if( !refs && requestBody?.content?.["application/json"]?.schema) {
    return noRefSchemaHandle(requestBody?.content?.["application/json"]?.schema,name);
  }
  if (refs) {
    requestBody = refsHandle(refs);
  }
  return wordsHandler(requestBody, [], name);
}

function noRefSchemaHandle(data,name) {
  const { type ,items} = data || {}
  if(type === 'array') {
    const {  format } = items || {}
    if(format) {
      return singleTsType(`Array<${typeEnum[format] || "any"}>`,name);
    }
    return  singleTsType(`Array<any>`,name)  ;
  }
}

function typesArrayHandle(data, extra = []) {
  const properties = {};
  if (Array.isArray(data)) {
    data.forEach((item) => {
      if (!extra.some((i) => i === item.name)) {
        properties[item.name] = item;
      }
    });
    return properties;
  }
  return data;
}

function refsHandle(str,name) {
  const componentsKey = str.split("/").pop();
  const items = globalComponents.schemas?.[componentsKey] || {};
  const { required: requiredArray = [], properties = {}, type } = items;

  // console.log(properties, "+++++properties++++++", str, componentsKey);
  Object.entries(properties).forEach(([key, value]) => {
    const { item = {}, ...others } = value;
    // console.log('others++++',others)
    let innerProperties = null;
    if (item["$ref"]) {
      innerProperties = refsHandle(item["$ref"]);
    }

    if (others?.items?.["$ref"]) {
      if(others?.items?.["$ref"] === str){
        others.items = {repeatName: name};
      }else{
        innerProperties = refsHandle(others?.items?.["$ref"]);
      }
    }

    let t = others.type;
    if (!t) {
      if (others.format) t = typeEnum[others.format];
      else t = "any";
    }

    properties[key] = {
      ...others,
      type: t,
      required: requiredArray?.some((s) => s === key) || false,
    };
    // console.log("innerProperties", innerProperties, key, properties[key]);
    if (innerProperties) {
      properties[key].schema = {
        properties: innerProperties,
      };
    }
  });
  return properties;
}

function typesHandle(data, extra) {
  const properties = {};
  if (typeof data !== "object") return;
  const obj = typesArrayHandle(data, extra);
  // console.log('130----',obj)
  Object.entries(obj).forEach(([key, value]) => {
    const { schema = {} } = value;
    let innerProperties = null;
    if (schema["$ref"]) {
      // console.log('schema["$ref"]', schema["$ref"]);
      innerProperties = refsHandle(schema["$ref"]);
      Object.entries(innerProperties).forEach(([inner, innerValue]) => {
        const { items = {} } = innerValue;
        if (items["$ref"]) {
          innerProperties[inner] = {
            ...innerValue,
            schema: {
              properties: refsHandle(items["$ref"]),
            },
          };
        }
      });
      // console.log("innerProperties", innerProperties);
    }
    // console.log('150----',value.type)
    properties[key] = {
      ...value,
      type: value.type || schema.type,
    };
    if (innerProperties) {
      properties[key].schema = {
        properties: innerProperties,
      };
    }
  });
  return properties;
}

function checkFolderExist() {
  fs.mkdir(options.fileFolder, (err) => {
    if (err) {
      if (err.code === "EEXIST") {
        // console.log("文件夹已存在");
      } else {
        console.error("创建文件夹时出错:", err);
      }
      return;
    }
    console.log("文件夹创建成功");
  });
}

//写入type到文件
function generateTypeFile(data) {
  writeFile(`${options.fileFolder}types.ts`, data);
}
function generateApiFile(data) {
  const { apiHead, apiClientType } = options;
  const tsImport = `

import { ${globalTypeImport.join(",")},${globalResTypeImport.join(
    ","
  )}} from './types'  
  `;
  data =
    `
${apiHead ? apiHead : ""}
${tsImport}

${
  apiClientType || `
interface RequestClient  {
  get<TResponse, TParams = void>(config: { url: string; params?: TParams }): Promise<TResponse>;
  post<TResponse,TParams = void, TBody = void>(config: { url: string;params?: TParams, data?: TBody }): Promise<TResponse>;
  put<TResponse,TParams = void, TBody = void>(config: { url: string;params?: TParams, data?: TBody }): Promise<TResponse>;
  patch<TResponse,TParams = void, TBody = void>(config: { url: string; params?: TParams,data?: TBody }): Promise<TResponse>;
  delete<TResponse, TParams = void>(config: { url: string; params?: TParams }): Promise<TResponse>;
}  
  
  `
}
  ` + data;
  writeFile(`${options.fileFolder}api.ts`, data);
}

function writeFile(fileName, data) {
  // 写入文件
  // console.log("fileName++++", fileName);
  fs.writeFile(fileName, data, "utf8", (err) => {
    if (err) {
      console.error("写入文件时出错：", err);
      return;
    }
    // console.log(`文件${fileName}写入成功！`);
  });
}

//组成type
function singleTsType(string,name) {
  return `
export type ${name} = ${string};`
}

// 组成 interface
function wordsHandler(obj, requiredList, name) {
  // console.log("258-----", obj, requiredList, name);
  let res = `
export interface ${name} {`;

  res += `
  ${singleWordsHandle(obj, requiredList)}
}`;
  return res;
}

function singleWordsHandle(obj, requiredList) {
  let res = "";
  const keys = Object.keys(obj || {});
  keys.forEach((key) => {
    const item = obj[key] || {};
    const required = requiredList.some((i) => i === key);
    if (item.type === "array") {
      // console.log('220*********',item)
      item.type = typeArrayHandle(item.items || item.schema?.items);
    }
    if (item.type === "integer") {
      item.type = item.format ? typeEnum[item.format] : 'number'
    };
    if (item?.schema?.properties) {
      const r = singleWordsHandle(item?.schema?.properties, []);
      item.type = `{
          ${r}
}`;
    }
    const title = item.title || item.description || "";
    res += `
  // ${title.replace(/[\n*\/]/g, " ").trim()}
  ${key}${item.required || required ? "" : "?"}: ${item.type};
    `;
  });
  return res;
}

// type为数组的处理
function typeArrayHandle(item) {
  let t = item.type;
  if (item.type === "array") {
    t = typeArrayHandle(item.items || {});
  }
  // console.log("typeArrayHandle",item)
  t = typeEnum[t] || t || item.repeatName || 'any';  //
  return `Array<${t}>`;
}
function capitalizeFirstLetter(str) {
  if (str === "") return "";
  return str[0].toUpperCase() + str.slice(1);
}

//获取请求URL  主要用于${}处理
function getMethodUrl(key) {
  const { requestUrlFilter = [] } = options;
  requestUrlFilter.forEach((item) => {
    key = key.replace(item, "");
  });
  const regex = /\{([^}]+)\}/g;

  const extractedValues = [];

  const newStr = key.replace(regex, (match, capture) => {
    extractedValues.push(capture);
    return `\${${capture}}`;
  });
  return { url: newStr, extra: extractedValues };
}

// 获取完整的请求
function getAllMethod({
  key,
  method,
  summary,
  requestType,
  requestBody,
  responseType,
}) {
  const { url, extra } = getMethodUrl(key);
  const { apiHead } = options;
  // let dataWords = "data";
  // if (method === "get") dataWords = "params";
  const extraContent = extra.length ? `${extra.join(",")},` : "";
  const newUrl = "`" + url + "`";
  const responseTs = `:Promise<${responseType || "any"}>`;
  const params = `${requestType ? `params:${requestType || "any"},` : ""}`;
  const requestData = `${requestBody ? `data:${requestBody || "any"},` : ""}`;

  const r = `
/**
 * @description ${summary} 
*/
export const ${method}${convertToCamelCase(key)}Api = ${
    apiHead ? "" : "<T extends RequestClient >"
  }(${
    apiHead ? "" : "request:T,"
  }${extraContent}${params}${requestData}others={})${responseTs} => {
    return request.${method}({
      url: ${newUrl},
      ${requestType ? "params," : ""}
      ${requestBody ? "data," : ""}
      ...others
    });
};
  `;
  return r;
}

function convertToCamelCase(str) {
  let newStr = str.replace(/[{}]/g, "");
  const parts = newStr.split("/");
  const camelCaseParts = parts.map((part, index) => {
    if (index === 0) {
      return part;
    }
    return part.charAt(0).toUpperCase() + part.slice(1);
  });
  return camelCaseParts.join("");
}

function convertToFirstCase(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

module.exports = {
  init,
};
